/*
 * Created on 2005-3-20
 *
 */
package com.hd123.rumba.sql;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;
import net.sf.hibernate.Session;
import net.sf.hibernate.cfg.Settings;
import net.sf.hibernate.hql.QueryTranslator;
import net.sf.hibernate.impl.SessionFactoryImpl;

import com.hd123.rumba.util.Iterators;

/**
 * @author caili
 */
public class HibernateSelect extends SelectExecutorBase implements SelectExecutor {
  private Session session;

  /**
   * @param connection
   * @param query
   * @param pageSize
   *          &lt;=0 则结果不分页
   */
  public HibernateSelect(Session session, SQLSubquery query, int pageSize) {
    this.session = session;
    this.query = query;
    try {
      super.connection = session.connection();
    } catch (Exception e) {
      logger.error(e.getMessage());
    }
  }

  /**
   * 执行SELECT, 返回从start(zero-based)开始的size条记录.
   * 
   * @param
   * @return
   * @throws
   */
  public List getContentList(int start, int size) throws HibernateException {
    Date st = new Date();
    List list = new LinkedList();
    String statement = query.getSqlStatement();
    exeStatement = statement;
    Vector parameters = query.getParametersVector();
    exeParameters = parameters;

    String perfConstruct = Long.toString(new Date().getTime() - st.getTime());
    st = new Date();

    Query hq = session.createQuery(statement);
    for (int k = 0; k < parameters.size(); ++k) {
      hq.setParameter(k, parameters.get(k));
    }

    logger.debug(exeStatement);
    logger.debug(exeParameters);
    String perfPrepare = Long.toString(new Date().getTime() - st.getTime());
    st = new Date();

    try {
      if (size > 0) {
        hq.setFirstResult(start < 0 ? 0 : start);
        hq.setMaxResults(size);
      }
      list = hq.list();
    } catch (HibernateException e) {
      throw e;
    }

    String perfExecute = Long.toString(new Date().getTime() - st.getTime());
    logger.debug("select: " + perfConstruct + "|" + perfPrepare + "|" + perfExecute);

    return list;
  }

  public Iterator getContentIterator(int start, int size) throws HibernateException {
    Date st = new Date();
    Iterator iterator = Iterators.createEmptyIterator();
    String statement = query.getSqlStatement();
    exeStatement = statement;
    Vector parameters = query.getParametersVector();
    exeParameters = parameters;

    String perfConstruct = Long.toString(new Date().getTime() - st.getTime());
    st = new Date();

    Query hq = session.createQuery(statement);
    for (int k = 0; k < parameters.size(); ++k) {
      hq.setParameter(k, parameters.get(k));
    }

    logger.debug(exeStatement);
    logger.debug(exeParameters);
    String perfPrepare = Long.toString(new Date().getTime() - st.getTime());
    st = new Date();

    try {
      if (size > 0) {
        hq.setFirstResult(start);
        hq.setMaxResults(size);
      }
      iterator = hq.iterate();
    } catch (HibernateException e) {
      throw e;
    }

    String perfExecute = Long.toString(new Date().getTime() - st.getTime());
    logger.debug("select: " + perfConstruct + "|" + perfPrepare + "|" + perfExecute);

    return iterator;
  }

  /**
   * 取得结果集的记录数
   * 
   * @return
   * @throws
   */
  public String getCountStatement(SQLSubquery query) throws Exception {
    if (query.getGroupClause().toString().trim().length() != 0
        || query.getSelectClause().toString().toLowerCase().indexOf(" distinct ") >= 0
        || query.getSelectClause().toString().toLowerCase().indexOf(" count(") >= 0
        || query.getSelectClause().toString().toLowerCase().indexOf(" sum(") >= 0
        || query.getSelectClause().toString().toLowerCase().indexOf(" avg(") >= 0
        || query.getSelectClause().toString().toLowerCase().indexOf(" min(") >= 0
        || query.getSelectClause().toString().toLowerCase().indexOf(" max(") >= 0) {
      Vector orders = (Vector)query.getOrderClause().getItems().clone();
      query.getOrderClause().getItems().clear();
      String stm = hqlToSql(session, query.getSqlStatement()) ;
      query.getOrderClause().setItems(orders);
      return "select count(*) from (" + stm + ") o";
    }
    return hqlToSql(session, query.getCountStatement());
  }

  /**
   * 取得queryString所产生的sqlString. 假设不是多态查询, 只会产生一条sql.
   * 
   * @param sess
   * @param queryString
   * @return
   * @throws Exception
   */
  public String hqlToSql(Session sess, String queryString) throws Exception {
    SessionFactoryImpl sfi = (SessionFactoryImpl) sess.getSessionFactory();
    Field field = sfi.getClass().getDeclaredField("settings");
    field.setAccessible(true);
    Settings settings = (Settings) field.get(sfi);

    String[] concreteQueryStrings = QueryTranslator.concreteQueries(queryString, sfi);
    if (concreteQueryStrings.length > 1) {
      StringBuffer sb = new StringBuffer();
      sb.append("此HQL查询将产生多条SQL查询, 无法处理:\n");
      for (int i = 0; i < concreteQueryStrings.length; ++i)
        sb.append(concreteQueryStrings[i] + "\n");
      // logger.error(sb.toString());
      throw new Exception(sb.toString());
    }
    QueryTranslator qt = new QueryTranslator(concreteQueryStrings[0]);
    qt.compile(sfi, settings.getQuerySubstitutions(), false);

    field = qt.getClass().getDeclaredField("sqlString");
    field.setAccessible(true);
    String sql = (String) field.get(qt);
    logger.debug("hqlToSql: " + sql);
    return sql;
  }

  /**
   * HQL不支持select ... from (select ...)语法, 不能用这个东东.
   * 
   * @deprecated
   * @return
   * @throws HibernateException
   */
  public int getCountOld() throws HibernateException {
    Date st = new Date();
    String statement = query.getCountStatement();
    exeStatement = statement;
    Vector parameters = query.getParametersVector();
    exeParameters = parameters;
    int rowcount = 0;

    String perfConstruct = Long.toString(new Date().getTime() - st.getTime());
    st = new Date();

    Query hq = session.createQuery(statement);
    for (int k = 0; k < parameters.size(); ++k) {
      hq.setParameter(k, parameters.get(k));
    }

    logger.debug(exeStatement);
    logger.debug(exeParameters);
    String perfPrepare = Long.toString(new Date().getTime() - st.getTime());
    st = new Date();

    try {
      rowcount = ((Integer) hq.list().get(0)).intValue();
    } catch (HibernateException e) {
      throw e;
    }

    String perfExecute = Long.toString(new Date().getTime() - st.getTime());
    logger.debug("count: " + perfConstruct + "|" + perfPrepare + "|" + perfExecute + "  result = "
        + rowcount);
    return rowcount;
  }
}
